package de.alfenory.autobill.logic;

import de.alfenory.autobill.config.TypeConfiguration;
import de.alfenory.autobill.config.DateConfiguration;
import de.alfenory.autobill.config.AutobillConfiguration;
import de.alfenory.autobill.entity.*;

import java.text.*;
import java.util.*;
import java.util.logging.*;

import java.util.GregorianCalendar;

/**
 * @author alexander.fenske@alfeno<ry.de
 */
public class OrderFacade {

    private static final Logger log = Logger.getLogger(OrderFacade.class.getName());

    /**
     * @param oiList
     * @param partsConfig
     * @param transdate
     * @param dateConfig
     * @param afterTermBilling
     * @return
     */
    public static List<OrderItem> getAllowedParts(List<Order> orderList, AutobillConfiguration autobillConfig, TypeConfiguration typeConfig, DateConfiguration dateConfig, boolean afterTermBilling) {
        List<OrderItem> list = new ArrayList<OrderItem>();
        for (Order order : orderList) { //Looking for every order
            if (!afterTermBilling) { //Checking whether order is before term
                if (order.getType() == typeConfig.getMapping_beforeterm_monthly()) {
                    for (OrderItem oi : order.getOrderItemList()) {
                        if (!autobillConfig.getNotAllowedPartsId().contains(oi.getPartId())) {
                            oi = addServicePeriod(order, oi, autobillConfig, dateConfig, false, 1);
                            list.add(oi);
                        }
                    }
                }
                if (order.getType() == typeConfig.getMapping_beforeterm_quarterly()) {
                    Calendar cal = Calendar.getInstance();
                    cal.setTime(order.getBeginn());
                    if (cal.get(Calendar.MONTH) % 3 == (dateConfig.getMonth()) % 3) {
                        for (OrderItem oi : order.getOrderItemList()) {
                            if (!autobillConfig.getNotAllowedPartsId().contains(oi.getPartId())) {
                                oi = addServicePeriod(order, oi, autobillConfig, dateConfig, false, 3);
                                list.add(oi);
                            }
                        }
                    }
                }
                if (order.getType() == typeConfig.getMapping_beforeterm_halfyearly()) {
                    Calendar cal = Calendar.getInstance();
                    cal.setTime(order.getBeginn());
                    if (cal.get(Calendar.MONTH) % 6 == (dateConfig.getMonth()) % 6) {
                        for (OrderItem oi : order.getOrderItemList()) {
                            if (!autobillConfig.getNotAllowedPartsId().contains(oi.getPartId())) {
                                oi = addServicePeriod(order, oi, autobillConfig, dateConfig, false, 6);
                                list.add(oi);
                            }
                        }
                    }
                }
                if (order.getType() == typeConfig.getMapping_beforeterm_yearly()) {
                    Calendar cal = Calendar.getInstance();
                    cal.setTime(order.getBeginn());
                    if (cal.get(Calendar.MONTH) % 12 == (dateConfig.getMonth()) % 12) {
                        for (OrderItem oi : order.getOrderItemList()) {
                            if (!autobillConfig.getNotAllowedPartsId().contains(oi.getPartId())) {
                                oi = addServicePeriod(order, oi, autobillConfig, dateConfig, false, 12);
                                list.add(oi);
                            }
                        }
                    }
                }
                if (order.getType() == typeConfig.getMapping_beforeterm_twoyearly()) {
                    Calendar cal = Calendar.getInstance();
                    cal.setTime(order.getBeginn());
                    if (cal.get(Calendar.MONTH) % 24 == (dateConfig.getMonth()) % 24) {
                        for (OrderItem oi : order.getOrderItemList()) {
                            if (!autobillConfig.getNotAllowedPartsId().contains(oi.getPartId())) {
                                oi = addServicePeriod(order, oi, autobillConfig, dateConfig, false, 24);
                                list.add(oi);
                            }
                        }
                    }
                }
                if (order.getType() == typeConfig.getMapping_beforeterm_tenyearly()) {
                    Calendar cal = Calendar.getInstance();
                    cal.setTime(order.getBeginn());
                    if (cal.get(Calendar.MONTH) % 120 == (dateConfig.getMonth()) % 120) {
                        for (OrderItem oi : order.getOrderItemList()) {
                            if (!autobillConfig.getNotAllowedPartsId().contains(oi.getPartId())) {
                                oi = addServicePeriod(order, oi, autobillConfig, dateConfig, false, 120);
                                list.add(oi);
                            }
                        }
                    }
                }
            } else {
                if (order.getType() == typeConfig.getMapping_afterterm_monthly()) {
                    for (OrderItem oi : order.getOrderItemList()) {
                        if (!autobillConfig.getNotAllowedPartsId().contains(oi.getPartId())) {
                            oi = addServicePeriod(order, oi, autobillConfig, dateConfig, true, 1);
                            list.add(oi);
                        }
                    }
                }
                if (order.getType() == typeConfig.getMapping_afterterm_quarterly()) {
                    Calendar cal = Calendar.getInstance();
                    cal.setTime(order.getBeginn());
                    if (cal.get(Calendar.MONTH) % 3 == (dateConfig.getMonth() - 1) % 3) {
                        for (OrderItem oi : order.getOrderItemList()) {
                            if (!autobillConfig.getNotAllowedPartsId().contains(oi.getPartId())) {
                                oi = addServicePeriod(order, oi, autobillConfig, dateConfig, true, 3);
                                list.add(oi);
                            }
                        }
                    }
                }
                if (order.getType() == typeConfig.getMapping_afterterm_halfyearly()) {
                    Calendar cal = Calendar.getInstance();
                    cal.setTime(order.getBeginn());
                    if (cal.get(Calendar.MONTH) % 6 == (dateConfig.getMonth() - 1) % 6) {
                        for (OrderItem oi : order.getOrderItemList()) {
                            if (!autobillConfig.getNotAllowedPartsId().contains(oi.getPartId())) {
                                oi = addServicePeriod(order, oi, autobillConfig, dateConfig, true, 6);
                                list.add(oi);
                            }
                        }
                    }
                }
                if (order.getType() == typeConfig.getMapping_afterterm_yearly()) {
                    Calendar cal = Calendar.getInstance();
                    cal.setTime(order.getBeginn());
                    if (cal.get(Calendar.MONTH) % 12 == (dateConfig.getMonth() - 1) % 12) {
                        for (OrderItem oi : order.getOrderItemList()) {
                            if (!autobillConfig.getNotAllowedPartsId().contains(oi.getPartId())) {
                                oi = addServicePeriod(order, oi, autobillConfig, dateConfig, true, 12);
                                list.add(oi);
                            }
                        }
                    }
                }
                if (order.getType() == typeConfig.getMapping_afterterm_twoyearly()) {
                    Calendar cal = Calendar.getInstance();
                    cal.setTime(order.getBeginn());
                    if (cal.get(Calendar.MONTH) % 24 == (dateConfig.getMonth() - 1) % 24) {
                        for (OrderItem oi : order.getOrderItemList()) {
                            if (!autobillConfig.getNotAllowedPartsId().contains(oi.getPartId())) {
                                oi = addServicePeriod(order, oi, autobillConfig, dateConfig, true, 24);
                                list.add(oi);
                            }
                        }
                    }
                }
                if (order.getType() == typeConfig.getMapping_afterterm_tenyearly()) {
                    Calendar cal = Calendar.getInstance();
                    //cal.setTime(dateConfig.getCurrentDate());
                    cal.setTime(order.getBeginn());
                    if (cal.get(Calendar.MONTH) % 120 == (dateConfig.getMonth() - 1) % 120) {
                        for (OrderItem oi : order.getOrderItemList()) {
                            if (!autobillConfig.getNotAllowedPartsId().contains(oi.getPartId())) {
                                oi = addServicePeriod(order, oi, autobillConfig, dateConfig, true, 120);
                                list.add(oi);
                            }
                        }
                    }
                }
            }
        }

        return list;
    }

    /**
     * Adds Service Period to a Orderitems Description. Return f.e. for
     * oiDescrition:Serverhousing, dateConfig:jan/2010, afterTermBilling:true,
     * period:1 = Serverhousing (2010-01-01 - 2010-01-31)
     *
     * @param oiDescription Orderitems Description
     * @param dateConfig Date Configuration
     * @param afterTermBilling is after Termin Billing
     * @param period a number of 1 for month, 3 for a quarter, 6 for half a year, 12 for a year, 120 for ten years
     * @return new Orderitems Description with service period.
     */
    public static OrderItem addServicePeriod(Order order, OrderItem orderItem, AutobillConfiguration autobillConfig, DateConfiguration dateConfig, boolean afterTermBilling, int period) {
        if (!dateConfig.isAddServicePeriod()) {
            return orderItem;
        }
        if (!orderItem.isAddServicePeriod()) {
            return orderItem;
        }
        Calendar cal = GregorianCalendar.getInstance();
        cal.setTime(afterTermBilling && !autobillConfig.isDailyMode() ? dateConfig.getDateAfterTerm() : dateConfig.getDateBeforeTerm());

        int month1 = cal.get(Calendar.MONTH);
        int year1 = cal.get(Calendar.YEAR);
        int month2 = month1;
        int year2 = year1;

        if (afterTermBilling) {
            month1 = month2 + 1 - period;
        } else {
            month2 = month1 - 1 + period;
        }

        if (month1 < 0) {
            month1 = 12 + month1;
            year1--;
        }
        if (month2 > 11) {
            month2 = month2 - 12;
            year2++;
        }

        SimpleDateFormat formatter = new SimpleDateFormat(dateConfig.getDateFormat());

        Date date1 = DateConfiguration.getCalendarFirstDayOfMonth(month1, year1).getTime();
        Date date2 = DateConfiguration.getCalendarLastDayOfMonth(month2, year2).getTime();

        Calendar beginnCal = Calendar.getInstance();
        beginnCal.setTime(order.getBeginn());
        Calendar endCal = Calendar.getInstance();
        endCal.setTime(order.getEnd());

        //Beforeterm: A Day of Month till Day in Month (dailybilling)
        if (
            autobillConfig.isDailyMode() &&
            !afterTermBilling &&
            order.getBeginn().getTime() <= dateConfig.getCurrentDate().getTime() &&
            order.getBeginn().getTime() >= dateConfig.getCurrentDateMinusPeriodePlus1Day(period).getTime()
        ) {
            Calendar cal2 = DateConfiguration.getCalendarLastDayOfMonth(month1, year1);
            Date date3 = order.getBeginn();
            int days = beginnCal.get(Calendar.DAY_OF_MONTH);
            int max_days = cal2.get(Calendar.DAY_OF_MONTH);
            int rest = autobillConfig.getDaysPerYear() % 360;
            if (date1.getTime() <= order.getBeginn().getTime() && max_days > 30 && rest == 0) {
                max_days = 30;
            }
            double diff_periode = 0;
            if(date1.getTime() > order.getBeginn().getTime() && rest == 0) { //Starts before period
                long diffdays = (date1.getTime() - order.getBeginn().getTime()) / (1000 * 60 * 60 * 24);
                diff_periode = (double)(diffdays) / (double)(dateConfig.getDaysOfPreviousPeriode(date1, period));
                days = 1;
            }
            date1 = order.getBeginn();
            double part = (double) (max_days - days + 1) / (double) max_days + diff_periode;
            orderItem.setQuantity(part * orderItem.getQuantity());
        }

        //Beforeterm: First Day of a LastMonth till Day in End Month (no dailybilling)
        if (endCal.get(Calendar.MONTH) == dateConfig.getMonth() && endCal.get(Calendar.YEAR) == dateConfig.getYear() && order.isEnding() && !afterTermBilling && !autobillConfig.isDailyMode()) {
            int max_days = beginnCal.getActualMaximum(Calendar.DAY_OF_MONTH);
            int rest = autobillConfig.getDaysPerYear() % 360;
            if (max_days > 30 && rest == 0) {
                max_days = 30;
            }
            int days = endCal.get(Calendar.DAY_OF_MONTH);
            double part = (double) (days) / (double) max_days;
            if (part > 1.0) {
                part = 1.0;
            }

            orderItem.setQuantity(part * orderItem.getQuantity());
            date2 = order.getEnd();
        }

        //AfterTerm; nodailymode
        if (month1 == beginnCal.get(Calendar.MONTH) && year1 == beginnCal.get(Calendar.YEAR) && afterTermBilling && !autobillConfig.isDailyMode()) {
            date1 = order.getBeginn();
            int days = beginnCal.get(Calendar.DAY_OF_MONTH);
            int max_days = DateConfiguration.getCalendarLastDayOfMonth(month1, year1).get(Calendar.DAY_OF_MONTH);
            int rest = autobillConfig.getDaysPerYear() % 360;
            if (max_days > 30 && rest == 0) {
                max_days = 30;
            }
            double part = (double) (max_days - days + 1) / (double) max_days;
            if (part > 1) {
                part = 1.0;
            }
            orderItem.setQuantity(part * orderItem.getQuantity());
        }

        //AfterTerm; dailymode
        if (order.getEnd().getTime() == dateConfig.getCurrentDate().getTime() && order.isEnding() && afterTermBilling && autobillConfig.isDailyMode()) {
            date2 = order.getEnd();
            int days = dateConfig.getDay();
            int max_days = DateConfiguration.getCalendarLastDayOfMonth(month1, year1).get(Calendar.DAY_OF_MONTH);
            int rest = autobillConfig.getDaysPerYear() % 360;
            if (max_days > 30 && rest == 0) {
                max_days = 30;
            }
            double part = (double) (days) / (double) max_days;
            if (part > 1) {
                part = 1.0;
            }
            orderItem.setQuantity(part * orderItem.getQuantity());
        }

        String dateFormat1 = formatter.format(date1);
        String dateFormat2 = formatter.format(date2);

        orderItem.setDescription(orderItem.getDescription() + "\n" + autobillConfig.getServicePeriodText() + dateFormat1 + " - " + dateFormat2);
        return orderItem;
    }

    public static boolean orderIsInTime(Order order, AutobillConfiguration autobillConfig, TypeConfiguration typeConfig, DateConfiguration dateConfig) {
        /*
         * if beginn date is lesser then enddate there should not be a bill for it, 
         * this gives user the posibility to add orders that will not be billed 
         * */
        if (order.getBeginn().getTime() > order.getEnd().getTime()) {
            return false;
        }

        if (typeConfig.isAfterTerm(order.getType())) { //billing after term
            if (order.getBeginn().getTime() < dateConfig.getCurrentDate().getTime()
                    && autobillConfig != null
                    && !autobillConfig.isDailyMode()
                    && (order.getEnd().getTime() >= dateConfig.getCurrentDate().getTime()
                    || order.isEnding() == false)) {
                return true;
            }
            if (autobillConfig != null && autobillConfig.isDailyMode() && order.getEnd().getTime() == dateConfig.getCurrentDate().getTime() && order.isEnding()) {
                return true;
            }
        } else { //billing before term
            if (order.getBeginn().getTime() < dateConfig.getCurrentDatePlus1Month().getTime()
                    && autobillConfig != null
                    && !autobillConfig.isDailyMode()
                    && (order.getEnd().getTime() >= dateConfig.getCurrentDatePlus2Month().getTime()
                    || order.isEnding() == false)) {
                return true;
            }
            if (
                order.getBeginn().getTime() <= dateConfig.getCurrentDate().getTime() && 
                order.getBeginn().getTime() >= dateConfig.getCurrentDateMinusPeriodePlus1Day(typeConfig.getPeriode(order.getType())).getTime()
            ){ //Check whether beginn Date is in billing period
                return true;
            }
            Calendar endCal = Calendar.getInstance();
            endCal.setTime(order.getEnd());
            if (!autobillConfig.isDailyMode() && endCal.get(Calendar.MONTH) == dateConfig.getMonth() && endCal.get(Calendar.YEAR) == dateConfig.getYear() && dateConfig.getDay() == 1) {
                return true;
            }
        }
        return false;
    }

    public static List<Order> getOrder(List<Order> orderList, AutobillConfiguration autobillConfig, DateConfiguration dateConfig, TypeConfiguration typeConfiguration, boolean afterterm) {
        List<Order> orderList1 = new ArrayList<Order>();
        for (Order order : orderList) {
            System.out.print(order.getBeginn()+"/"+order.getText()+"/");
            if (orderIsInTime(order, autobillConfig, typeConfiguration, dateConfig)) { //is in time
                    orderList1.add(order);
            }
        }
        return orderList1;
    }
}